home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 March / Macworld (1998-03) (Disk 1).dmg / Shareware World / Info / For Developers / GhostScript 5.10 / MacGS-510 / files / pdf_base.ps < prev    next >
Text File  |  1997-09-25  |  15KB  |  457 lines

  1. %    Copyright (C) 1994, 1996, 1997 Aladdin Enterprises.  All rights reserved.
  2. % This file is part of Aladdin Ghostscript.
  3. % Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  4. % or distributor accepts any responsibility for the consequences of using it,
  5. % or for whether it serves any particular purpose or works at all, unless he
  6. % or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  7. % License (the "License") for full details.
  8. % Every copy of Aladdin Ghostscript must include a copy of the License,
  9. % normally in a plain ASCII text file named PUBLIC.  The License grants you
  10. % the right to copy, modify and redistribute Aladdin Ghostscript, but only
  11. % under certain conditions described in the License.  Among other things, the
  12. % License requires that the copyright notice and this notice be preserved on
  13. % all copies.
  14.  
  15. % pdf_base.ps
  16. % Basic parser for PDF reader.
  17.  
  18. % This handles basic parsing of the file (including the trailer
  19. % and cross-reference table), as well as objects, object references,
  20. % and streams; it doesn't include any facilities for making marks on
  21. % the page.
  22.  
  23. /.setlanguagelevel where { pop 2 .setlanguagelevel } if
  24. .currentglobal true .setglobal
  25. /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
  26. pdfdict begin
  27.  
  28. % We rebind #, #?, #dsc, and #dscfile later if we're writing out PostScript.
  29. /#            % <arg1> ... <argN> <opname> <N> # -
  30.  { pop cvx exec
  31.  } bind def
  32. /#?
  33.  { false
  34.  } bind def
  35. /#dsc            % mark <obj1> ... #dsc -
  36.  { cleartomark
  37.  } bind def
  38. /#dscfile        % <filename> #dscfile -
  39.  { pop
  40.  } bind def
  41.  
  42. % Define the name interpretation dictionary for reading values.
  43. /valueopdict mark
  44.   (<<) cvn { mark } bind    % don't push an actual mark!
  45.   (>>) cvn /.dicttomark load
  46.   ([) cvn { mark } bind        % ditto
  47.   (]) cvn dup load
  48.   /true true
  49.   /false false
  50.   /null null
  51.   /F dup cvx        % see Objects section below
  52.   /R dup cvx        % see Objects section below
  53.   /stream dup cvx    % see Streams section below
  54. .dicttomark readonly def
  55.  
  56. % ------ Utilities ------ %
  57.  
  58. % Define a scratch string.  The PDF language definition says that
  59. % no line in a PDF file can exceed 255 characters.
  60. /pdfstring 255 string def
  61.  
  62. % Read the previous line of a file.  If we aren't at a line boundary,
  63. % read the line containing the current position.
  64. % Skip any blank lines.
  65. /prevline        % - prevline <startpos> <substring>
  66.  { PDFfile fileposition dup () pdfstring
  67.    2 index 257 sub 0 .max PDFfile exch setfileposition
  68.     {        % Stack: initpos linepos line string
  69.       PDFfile fileposition
  70.       PDFfile 2 index readline pop
  71.       dup length 0 gt
  72.        { 3 2 roll 5 -2 roll pop pop 2 index }
  73.        { pop }
  74.       ifelse
  75.         % Stack: initpos linepos line string startpos
  76.       PDFfile fileposition 5 index ge { exit } if
  77.       pop
  78.     }
  79.    loop pop pop 3 -1 roll pop
  80.  } bind def
  81.  
  82. % Read a token from a file, recognizing the PDF 1.2 #nn escape convention.
  83. % This should be done in C!
  84. /.pdftoken            % <file> .pdftoken <obj> -true-
  85.                 % <file> .pdftoken -false-
  86. { token
  87.    { dup type /nametype eq
  88.       { dup xcheck
  89.      { true
  90.      }
  91.      { dup .namestring (#) search
  92.         { name#escape cvn exch pop }
  93.         { pop }
  94.        ifelse true
  95.      }
  96.     ifelse
  97.       }
  98.       { true
  99.       }
  100.      ifelse
  101.    }
  102.    { false
  103.    }
  104.   ifelse
  105. } bind def
  106. /name#escape            % <post> <(#)> <pre> name#escape <string>
  107. { exch pop
  108.   1 index 2 () /SubFileDecode filter dup (x) readhexstring
  109.         % Stack: post pre stream char t/f
  110.   not { /.pdftoken cvx /syntaxerror signalerror } if
  111.   exch closefile concatstrings
  112.   exch 2 1 index length 2 sub getinterval
  113.   (#) search { name#escape } if concatstrings
  114. } bind def
  115.  
  116. % Execute a file, interpreting its executable names in a given
  117. % dictionary.  The name procedures may do whatever they want
  118. % to the operand stack.
  119. /.pdfrun            % <file> <opdict> .pdfrun -
  120.  {    % Construct a procedure with the stack depth, file and opdict
  121.     % bound into it.
  122.    1 index cvlit count 2 sub 3 1 roll mark mark 5 2 roll
  123.     {    % Stack: ..operands.. count opdict file
  124.       .pdftoken not { (%%EOF) cvn cvx } if
  125.       dup xcheck
  126.        { DEBUG { dup == flush } if
  127.      2 copy .knownget
  128.       { exch pop exch pop exch pop exec }
  129.       { BXlevel 0 le
  130.          { (%stderr) (w) file
  131.            dup (****************Unknown operator: ) writestring
  132.            dup 2 index .writecvs dup (\n) writestring flushfile
  133.          }
  134.         if pop pop
  135.         count exch sub { pop } repeat    % pop all the operands
  136.       }
  137.      ifelse
  138.        }
  139.        { exch pop exch pop DEBUG { dup ==only ( ) print flush } if
  140.        }
  141.       ifelse
  142.     }
  143.    aload pop .packtomark cvx
  144.    /loop cvx 2 packedarray cvx
  145.     { stopped /PDFsource } aload pop
  146.    PDFsource
  147.     { store { stop } if } aload pop .packtomark cvx
  148.    /PDFsource 3 -1 roll store exec
  149.  } bind def
  150.  
  151. % ------ File reading ------ %
  152.  
  153. % Read the cross-reference entry for an (unresolved) object.
  154. % The caller must save and restore the PDFfile position if desired.
  155. % For invalid (free) objects, we return 0.
  156. /readxrefentry        % <object#> readxrefentry <objpos>
  157.  { dup Objects exch lget
  158.    PDFfile exch setfileposition
  159.    PDFfile token pop        % object position
  160.    PDFfile token pop        % generation #
  161.    PDFfile token pop        % n or f
  162.    dup /n eq
  163.     { pop 1 add dup 255 gt
  164.        { Generations ltype /stringtype eq
  165.       {        % Convert Generations from a string to an array.
  166.         larray Generations llength lgrowto dup
  167.         0 1 2 index llength 1 sub
  168.          { Generations 1 index lget lput dup
  169.          }
  170.         for pop /Generations exch store
  171.       }
  172.      if
  173.        }
  174.       if
  175.     }
  176.     { /f eq
  177.        { pop 0 }
  178.        { /readxrefentry cvx /syntaxerror signalerror }
  179.       ifelse
  180.     }
  181.    ifelse
  182.         % Stack: obj# objpos 1+gen#
  183.    Generations 4 -1 roll 3 -1 roll lput
  184.  } bind def
  185.  
  186. % ================================ Objects ================================ %
  187.  
  188. % Since we may have more than 64K objects, we have to use a 2-D array to
  189. % hold them (and the parallel Generations structure).
  190. /lshift 9 def
  191. /lnshift lshift neg def
  192. /lsubmask 1 lshift bitshift 1 sub def
  193. /lsublen lsubmask 1 add def
  194. /larray {    % - larray <larray>
  195.   [ [] ]
  196. } bind def
  197. /lstring {    % - lstring <lstring>
  198.   [ () ]
  199. } bind def
  200. /ltype {    % <lseq> type <type
  201.   0 get type
  202. } bind def
  203. /lget {        % <lseq> <index> lget <value>
  204.   dup //lsubmask and 3 1 roll //lnshift bitshift get exch get
  205. } bind def
  206. /lput {        % <lseq> <index> <value> lput -
  207.   3 1 roll
  208.   dup //lsubmask and 4 1 roll //lnshift bitshift get
  209.   3 1 roll put
  210. } bind def
  211. /llength {    % <lseq> llength <length>
  212.   dup length 1 sub dup //lshift bitshift
  213.   3 1 roll get length add
  214. } bind def
  215. % lgrowto assumes newlength > llength(lseq)
  216. /growto {    % <string/array> <length> growto <string'/array'>
  217.   1 index type /stringtype eq { string } { array } ifelse
  218.   2 copy copy pop exch pop
  219. } bind def
  220. /lgrowto {    % <lseq> <newlength> lgrowto <lseq'>
  221.     dup //lsubmask add //lnshift bitshift dup 3 index length gt {
  222.     % Add more sub-arrays.  Start by completing the last existing one.
  223.         % Stack: lseq newlen newtoplen
  224.     3 -1 roll dup llength 1 sub //lsubmask or 1 add lgrowto
  225.         % Stack: newlen newtoplen lseq
  226.     [ exch aload pop
  227.     counttomark 2 add -1 roll        % newtoplen
  228.     counttomark sub { dup 0 0 getinterval lsublen growto } repeat
  229.     dup 0 0 getinterval ] exch
  230.   } {
  231.     pop
  232.   } ifelse
  233.     % Expand the last sub-array.
  234.   1 sub //lsubmask and 1 add
  235.   exch dup dup length 1 sub 2 copy
  236.         % Stack: newsublen lseq lseq len-1 lseq len-1
  237.   get 5 -1 roll growto put
  238. } bind def
  239.  
  240. % We represent an unresolved object reference by a procedure of the form
  241. % {obj# gen# resolveR}.  This is not a possible PDF object, because PDF has
  242. % no way to represent procedures.  Since PDF in fact has no way to represent
  243. % any PostScript object that doesn't evaluate to itself, we can 'force'
  244. % a possibly indirect object painlessly with 'exec'.
  245. % Note that since we represent streams by executable dictionaries
  246. % (see below), we need both an xcheck and a type check to determine
  247. % whether an object has been resolved.
  248. /unresolved?        % <object#> unresolved? <bool>
  249.  { Objects exch lget dup xcheck exch type /integertype eq and
  250.  } bind def
  251. /oforce /exec load def
  252. /oget        % <array> <index> oget <object>
  253.         % <dict> <key> oget <object>
  254.  { 2 copy get dup xcheck
  255.     { exec dup 4 1 roll put }
  256.     { exch pop exch pop }
  257.    ifelse
  258.  } bind def
  259. % A null value in a dictionary is equivalent to an omitted key;
  260. % we must check for this specially.
  261. /knownoget
  262.  { 2 copy known
  263.     { oget dup null eq { pop false } { true } ifelse }
  264.     { pop pop false }
  265.    ifelse
  266.  } bind def
  267.  
  268. % PDF 1.1 defines a 'foreign file reference', but not its meaning.
  269. % Per the specification, we convert these to nulls.
  270. /F        % <file#> <object#> <generation#> F <object>
  271.  {        % Some PDF 1.1 files use F as a synonym for f!
  272.    count 3 lt { f } { pop pop pop null } ifelse
  273.  } bind def
  274.  
  275. % We keep track of objects in a pair of arrays, Objects and Generations.
  276. % Generations[N] is 1+ the current generation number for object number N.
  277. % (As far as we can tell, this is needed only for error checking.)
  278. % If object N is loaded, Objects[N] is the actual object;
  279. % otherwise, Objects[N] is an executable integer giving the file offset
  280. % of the object's entry in the cross-reference table.
  281. % For free objects, Generations[N] is 0.
  282. /checkgeneration  % <object#> <generation#> checkgeneration <object#> <OK>
  283.  { Generations 2 index lget 1 sub 1 index eq
  284.     { pop true
  285.     }
  286.     { (Warning: wrong generation: ) print 1 index =only ( ) print = false
  287.     }
  288.    ifelse
  289.  } bind def
  290. /R        % <object#> <generation#> R <object>
  291.  { 1 index unresolved?
  292.     { /resolveR cvx 3 packedarray cvx }
  293.     { checkgeneration { Objects exch lget } { pop null } ifelse }
  294.    ifelse
  295.  } bind def
  296.  
  297. % If we encounter an object definition while reading sequentially,
  298. % we just store it away and keep going.
  299. /objopdict mark
  300.   valueopdict { } forall
  301.   /endobj dup cvx
  302. .dicttomark readonly def
  303. /obj            % <object#> <generation#> obj <object>
  304.  { PDFfile objopdict .pdfrun
  305.  } bind def
  306. /endobj            % <object#> <generation#> <object> endobj <object>
  307.  { 3 1 roll
  308.         % Read the xref entry if we haven't yet done so.
  309.         % This is only needed for generation # checking.
  310.    1 index unresolved?
  311.     { PDFfile fileposition
  312.       2 index readxrefentry pop
  313.       PDFoffset add PDFfile exch setfileposition
  314.     } if
  315.    checkgeneration { Objects exch 2 index lput } { pop pop null } ifelse
  316.  } bind def
  317.  
  318. % When resolving an object reference, we stop at the endobj.
  319. /resolveopdict mark
  320.   valueopdict { } forall
  321.   /endobj { endobj exit } bind
  322. .dicttomark readonly def
  323. /resolveR        % <object#> <generation#> resolveR <object>
  324.  { DEBUG { (%Resolving: ) print 2 copy 2 array astore == } if
  325.    1 index unresolved?
  326.     { PDFfile fileposition 3 1 roll
  327.       1 index readxrefentry
  328.       3 1 roll checkgeneration
  329.        {        % Stack: savepos objpos obj#
  330.      exch PDFoffset add PDFfile exch setfileposition
  331.      PDFfile token pop 2 copy ne
  332.       { (xref error!\n) print /resolveR cvx /rangecheck signalerror
  333.       }
  334.      if pop PDFfile token pop
  335.      PDFfile token pop /obj ne
  336.       { (xref error!\n) print /resolveR cvx /rangecheck signalerror
  337.       }
  338.      if
  339.      pdf_run_resolve    % PDFfile resolveopdict .pdfrun
  340.        }
  341.        { Objects exch null lput pop null
  342.        }
  343.       ifelse exch PDFfile exch setfileposition
  344.     }
  345.     { pop Objects exch lget
  346.     }
  347.    ifelse
  348.  } bind def      
  349.  
  350. %================================ Streams ================================ %
  351.  
  352. % We represent a stream by an executable dictionary that contains,
  353. % in addition to the contents of the original stream dictionary:
  354. %    /File - the file or string where the stream contents are stored;
  355. %    /FilePosition - iff File is a file, the position in the file
  356. %      where the contents start.
  357. %    /StreamKey - the key used to decrypt this stream if any
  358. % We do the real work of constructing the data stream only when the
  359. % contents are needed.
  360.  
  361. % Construct a stream.  The length is not reliable in the face of
  362. % different end-of-line conventions, but it's all we've got.
  363. %
  364. % PDF files are inconsistent about what may fall between the 'stream' keyword
  365. % and the actual stream data, and it appears that no one algorithm can
  366. % detect this reliably.  We used to try to guess whether the file included
  367. % extraneous \r and/or \n characters, but we no longer attempt to do so,
  368. % especially since the PDF 1.2 specification states flatly that the only
  369. % legal terminators following the 'stream' keyword are \n or \r\n, both of
  370. % which are properly skipped and discarded by the token operator.
  371. /stream
  372.  { PDFsource PDFfile eq
  373.     { dup /File PDFfile put
  374.       dup /FilePosition PDFfile fileposition put
  375.       DEBUG { (%FilePosition: ) print dup /FilePosition get == } if
  376.       PDFfile fileposition 1 index /Length oget add
  377.         PDFfile exch setfileposition
  378.     }
  379.     {    % We're already reading from a stream, which we can't reposition.
  380.     % Capture the sub-stream contents in a string.
  381.       dup /Length oget string PDFsource exch readstring
  382.       not
  383.        { (Unexpected EOF in stream!\n) print
  384.      /stream cvx /rangecheck signalerror
  385.        }
  386.       if
  387.       1 index exch /File exch put
  388.     }
  389.    ifelse
  390.    PDFsource token pop
  391.      /endstream ne { /stream cvx /syntaxerror signalerror } if
  392.    cvx
  393.  } bind def
  394.  
  395. % Resolve a stream dictionary to a PostScript stream.
  396. % Streams with no filters require special handling:
  397. %    - If we are going to interpret their contents, we let endstream
  398. %      terminate the interpretation loop;
  399. %    - If we are just going to read data from them, we impose
  400. %      a SubFileDecode filter that reads just the requisite amount of data.
  401. % Note that, in general, resolving a stream repositions PDFfile.
  402. % Clients must save and restore the position of PDFfile themselves.
  403. /resolvestream        % <streamdict> <readdata?> resolvestream <stream>
  404.  { exch dup /FilePosition .knownget
  405.     { 1 index /File get exch setfileposition }
  406.    if
  407.         % Stack: readdata? dict
  408.    dup /DecodeParms .knownget not { null } if
  409.    1 index /Filter .knownget not { {} } if
  410.    dup type /nametype eq
  411.     { 1 array astore
  412.       1 index null ne { exch 1 array astore exch } if
  413.     }
  414.    if
  415.         % Stack: readdata? dict parms filternames
  416.    2 index /File get exch
  417.         % Stack: readdata? dict parms file/string filternames
  418.    pdf_decrypt_stream        % add decryption if needed
  419.    dup length 0 eq
  420.     {        % All the PDF filters have EOD markers, but in this case
  421.         % there is no specified filter.
  422.       pop exch pop
  423.         % Stack: readdata? dict file/string
  424.       2 index
  425.        {    % We're going to read data; use a SubFileDecode filter.
  426.      1 index /Length oget () /SubFileDecode filter
  427.        }
  428.        { dup type /filetype ne
  429.       {    % Use a SubFileDecode filter to read from a string.
  430.         0 () SubFileDecode filter
  431.       }
  432.      if
  433.        }
  434.       ifelse
  435.     }
  436.     { 2 index null eq
  437.        { { filter }
  438.        }
  439.        {    % Stack: readdata? dict parms file/string filtername
  440.          { 2 index 0 get dup null eq { pop } { exch } ifelse filter
  441.        exch dup length 1 sub 1 exch getinterval exch
  442.      }
  443.        }
  444.       ifelse forall exch pop
  445.     }
  446.    ifelse
  447.         % Stack: readdata? dict file
  448.    exch pop exch pop
  449.  } bind def
  450. /endstream { exit } def
  451.  
  452. end            % pdfdict
  453. .setglobal
  454.